package org.mozhu.mboot.admin.personal.message.web.controller;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.mozhu.mboot.admin.AppUtils;
import org.mozhu.mboot.admin.personal.message.entiity.Message;
import org.mozhu.mboot.admin.personal.message.entiity.MessageState;
import org.mozhu.mboot.admin.personal.message.service.MessageService;
import org.mozhu.mboot.admin.system.account.entity.User;
import org.mozhu.mboot.admin.system.account.service.UserService;
import org.mozhu.mboot.admin.system.account.web.bind.annotation.CurrentUser;
import org.mozhu.mboot.core.data.Searchable;
import org.mozhu.mboot.core.security.Permissions;
import org.mozhu.mboot.core.support.datatable.DTReturnObject;
import org.mozhu.mboot.core.support.datatable.DTUtils;
import org.mozhu.mboot.core.util.BeanUtils;
import org.mozhu.mboot.core.util.MessageUtils;
import org.mozhu.mboot.core.web.AjaxResponse;
import org.mozhu.mboot.core.web.Constants;
import org.mozhu.mboot.core.web.bind.annotation.PageableDefaults;
import org.mozhu.mboot.core.web.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Controller
@RequestMapping("/personal/message")
public class MessageController extends BaseController {

    protected static final String OPERATION_SEND_DRAFT = "op.send.draft";
    protected static final String OPERATION_SEND_NEW = "op.send.new";
    protected static final String OPERATION_REPLY = "op.reply";
    protected static final String OPERATION_FORWARD = "op.forward";

    protected static final String SEND_SUCCESS = "op.send.success";
    protected static final String REPLY_SUCCESS = "op.reply.success";
    protected static final String FORWARD_SUCCESS = "op.forward.success";
    protected static final String MARK_AS_READ_SUCCESS = "op.mark.as.read.success";
    protected static final String SAVE_DRAFT_SUCCESS = "op.save.draft.success";
    protected static final String MOVE_TRASH_SUCCESS = "op.move.trash.success";
    protected static final String SAVE_STORE_SUCCESS = "op.save.store.success";
    protected static final String CLEAR_SUCCESS = "op.clear.success";

    Permissions permissions = Permissions.newPermissions("personal:message");

    @Autowired
    MessageService messageService;

    @Autowired
    UserService userService;

    @Override
    protected void setCommonData(Model model) {
        super.setCommonData(model);
        if (permissions != null) {
            model.addAttribute("resourceIdentity", permissions.getResourceIdentity());
        }
        // 供页面显示
        model.addAttribute("entityName", "message");
        model.addAttribute("states", MessageState.values());
    }

    // -- ajax list -- //
    @RequestMapping(value = "", method = RequestMethod.GET)
    @PageableDefaults(sort = "id=desc")
    public String listDefault(HttpServletRequest request, @CurrentUser User user, Pageable pageable, Model model) {
        return list(request, user, MessageState.in_box, model, "list");
    }

    @RequestMapping(value = "{state}/list", method = RequestMethod.GET)
    public String list(HttpServletRequest request, @CurrentUser User user, @PathVariable("state") MessageState state, Model model,
                       @RequestParam(value = "view", required = false, defaultValue = "list") String view) {
        if (permissions != null) {
            this.permissions.assertHasReadPermission();
        }
        model.addAttribute("state", state);
        setCommonData(model);
        return viewName(view);
    }

    @RequestMapping(headers = "ajax=true", method = RequestMethod.POST)
    @ResponseBody
    @PageableDefaults(sort = "id=desc")
    public Object listDefault(HttpServletRequest request, @CurrentUser User user, Model model, Searchable searchable) {
        return list(request, user, MessageState.in_box, model, searchable);
    }

    @RequestMapping(value = "{state}/list", headers = "ajax=true", method = RequestMethod.POST)
    @ResponseBody
    @PageableDefaults(sort = "id=desc")
    public Object list(HttpServletRequest request, @CurrentUser User user, @PathVariable("state") MessageState state, Model model, Searchable searchable) {
        if (permissions != null) {
            this.permissions.assertHasReadPermission();
        }
        model.addAttribute("state", state);
        Page<Message> page = messageService.findUserMessage(user.getId(), state, searchable);
        DTReturnObject<Message> dtReturnObject = DTUtils.toDTReturnObject(page);
        dtReturnObject.setDraw(request.getParameter("draw"));
        dtReturnObject.setExtraData(getExtraData(request, page));
        return dtReturnObject;
    }

    protected Map<String, Object> getExtraData(HttpServletRequest request, Page<Message> page) {
        Map<String, Object> extraDataMap = Maps.newHashMap();
        List<Message> authList = page.getContent();
        extraDataMap.put("users", getUsers(authList));
        return extraDataMap;
    }

    private Object getUsers(List<Message> messageList) {
        Set<Long> userIds = getRelatedUserIds(messageList);
        return userService.findAllById(userIds).stream().collect(Collectors.toMap(k -> k.getId().toString(), c -> BeanUtils.transBean2Map(c, "id", "name")));
    }

    private Set<Long> getRelatedUserIds(List<Message> messageList) {
        Set<Long> receiverIds = messageList.stream().map(a -> a.getReceiverId()).collect(Collectors.toSet());
        Set<Long> senderIds = messageList.stream().map(a -> a.getSenderId()).collect(Collectors.toSet());
        Set<Long> userIds = Sets.newHashSet();
        userIds.addAll(receiverIds);
        userIds.addAll(senderIds);
        return userIds;
    }

    @RequestMapping("{m}")
    public String view(@CurrentUser User user, @PathVariable("m") Message m, Model model, RedirectAttributes redirectAttributes) {
        if (m == null) {
            redirectAttributes.addFlashAttribute(Constants.ERROR, MessageUtils.message("message.not.exists"));
            return redirect("list");
        }

        if (user.getId().equals(m.getReceiverId())) {
            messageService.markRead(m);
        }

        // 得到消息之前的 和 之后的
        List<Message> messages = messageService.findAncestorsAndDescendants(m);
        model.addAttribute("messages", messages);
        List<Message> queryMessages = Lists.newArrayList(messages);
        queryMessages.add(m);
        model.addAttribute("users", getUsers(queryMessages));
        model.addAttribute("user", user);
        setCommonData(model);
        return viewName("view");
    }

    @RequestMapping(value = "/send", method = RequestMethod.GET)
    public String showSendForm(Model model) {
        if (!model.containsAttribute("m")) {
            model.addAttribute("m", newModel());
        }
        model.addAttribute(Constants.OP_NAME, OPERATION_SEND_NEW);
        setCommonData(model);
        return viewName("sendForm");
    }

    @RequestMapping(value = "/send", method = RequestMethod.POST)
    public String send(@CurrentUser User user, @RequestParam(value = "receiverId", required = false) Long receiverId, @Valid @ModelAttribute("m") Message message, BindingResult result, Model model,
                       RedirectAttributes redirectAttributes) {
        User receiver = userService.findById(receiverId);
        if (receiver == null) {
            result.rejectValue("receiverId", "receiver.not.exists");
        }
        if (receiver.equals(user)) {
            result.rejectValue("receiverId", "receiver.not.self");
        }

        if (result.hasErrors()) {
            return showSendForm(model);
        }
        message.setReceiverId(receiver.getId());
        message.setSenderId(user.getId());
        messageService.send(message);

        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(SEND_SUCCESS));
        return redirect(MessageState.out_box + "/list");
    }

    @RequestMapping(value = "/{parent}/reply", method = RequestMethod.GET)
    public String showReplyForm(@CurrentUser User user, @PathVariable("parent") Message parent, Model model) {
        if (!model.containsAttribute("m")) {
            Message m = newModel();
            m.setParentId(parent.getId());
            m.setParentIds(parent.getParentIds());
            m.setReceiverId(parent.getSenderId());
            m.setTitle(MessageUtils.message(MessageService.REPLY_TITLE_PREFIX, parent.getTitle()));
            model.addAttribute("m", m);
        }
        model.addAttribute(Constants.OP_NAME, OPERATION_REPLY);
        setCommonData(model);
        model.addAttribute("users", getUsers(Lists.newArrayList(parent)));
        model.addAttribute("user", user);
        return viewName("sendForm");
    }

    @RequestMapping(value = "/{parent}/reply", method = RequestMethod.POST)
    public String reply(@CurrentUser User user, @PathVariable("parent") Message parent, @ModelAttribute("m") Message m, BindingResult result, RedirectAttributes redirectAttributes, Model model) {
        if (result.hasErrors()) {
            return showReplyForm(user, parent, model);
        }
        m.setSenderId(user.getId());
        messageService.send(m);

        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(REPLY_SUCCESS));
        return redirect(MessageState.out_box + "/list");
    }

    @RequestMapping(value = "/{parent}/forward", method = RequestMethod.GET)
    public String showForwardForm(@CurrentUser User user, @PathVariable("parent") Message parent, Model model) {
        String receiverUsername = userService.findById(parent.getReceiverId()).getLoginName();
        String senderUsername = userService.findById(parent.getSenderId()).getLoginName();

        if (!model.containsAttribute("m")) {
            Message m = newModel();
            m.setTitle(MessageUtils.message(MessageService.FOWRARD_TITLE_PREFIX, parent.getTitle()));
            m.setContent(MessageUtils.message(MessageService.FOWRARD_CONTENT_TEMPLATE, senderUsername, receiverUsername, parent.getTitle(), parent.getContent()));
            model.addAttribute("m", m);
        }
        model.addAttribute("users", getUsers(Lists.newArrayList(parent)));
        model.addAttribute("user", user);
        model.addAttribute(Constants.OP_NAME, OPERATION_FORWARD);
        setCommonData(model);
        return viewName("sendForm");
    }

    @RequestMapping(value = "/{parent}/forward", method = RequestMethod.POST)
    public String forward(@CurrentUser User user, @RequestParam(value = "receiverId", required = false) Long receiverId, @PathVariable("parent") Message parent, @ModelAttribute("m") Message m,
                          BindingResult result, RedirectAttributes redirectAttributes, Model model) {
        User receiver = userService.findById(receiverId);
        if (receiver == null) {
            result.rejectValue("receiverId", "receiver.not.exists");
        }

        if (receiver.equals(user)) {
            result.rejectValue("receiverId", "receiver.not.self");
        }

        if (result.hasErrors()) {
            return showForwardForm(user, parent, model);
        }
        m.setReceiverId(receiver.getId());
        m.setSenderId(user.getId());
        messageService.send(m);

        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(FORWARD_SUCCESS));
        return redirect(MessageState.out_box + "/list");
    }

    @RequestMapping(value = "draft/save", method = RequestMethod.POST)
    public String saveDraft(@CurrentUser User user, @RequestParam(value = "receiverId", required = false) Long receiverId, @ModelAttribute("m") Message m, RedirectAttributes redirectAttributes) {
        User receiver = userService.findById(receiverId);
        if (receiver != null) {
            m.setReceiverId(receiver.getId());
        }
        m.setSenderId(user.getId());

        messageService.saveDraft(m);

        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(SAVE_DRAFT_SUCCESS));
        return redirect(MessageState.draft_box + "/list");
    }

    @RequestMapping(value = "draft/{m}/send", method = RequestMethod.GET)
    public String showResendDraftForm(@CurrentUser User user, @PathVariable("m") Message m, Model model) {
        if (m.getReceiverId() != null) {
            User receiver = userService.findById(m.getReceiverId());
            if (receiver != null) {
                model.addAttribute("receiverName", receiver.getName());
            }
        }
        model.addAttribute("m", m);
        model.addAttribute("users", getUsers(Lists.newArrayList(m)));
        model.addAttribute("user", user);
        String viewName = showSendForm(model);
        model.addAttribute(Constants.OP_NAME, OPERATION_SEND_DRAFT);
        setCommonData(model);
        return viewName;
    }

    @RequestMapping(value = "draft/{m}/send", method = RequestMethod.POST)
    public String resendDraft(@CurrentUser User user, @RequestParam(value = "receiverId", required = false) Long receiverId, @Valid @ModelAttribute("m") Message m, BindingResult result, Model model,
                              RedirectAttributes redirectAttributes) {
        String viewName = send(user, receiverId, m, result, model, redirectAttributes);
        model.addAttribute(Constants.OP_NAME, OPERATION_SEND_DRAFT);
        return viewName;
    }

    @RequestMapping("batch/store")
    public String batchStore(@CurrentUser User user, @RequestParam(value = "ids", required = false) Long[] ids, RedirectAttributes redirectAttributes) {
        messageService.store(user.getId(), ids);
        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(SAVE_STORE_SUCCESS));
        return redirect(MessageState.store_box + "/list");
    }

    @RequestMapping("batch/recycle")
    public String batchRecycle(@CurrentUser User user, @RequestParam(value = "ids", required = false) Long[] ids, RedirectAttributes redirectAttributes) {

        messageService.recycle(user.getId(), ids);

        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(MOVE_TRASH_SUCCESS));
        return redirect(MessageState.trash_box + "/list");
    }

    @RequestMapping("batch/delete")
    public String batchDelete(@CurrentUser User user, @RequestParam(value = "ids", required = false) Long[] ids, RedirectAttributes redirectAttributes) {
        messageService.delete(user.getId(), ids);
        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(DELETE_SUCCESS));
        return redirect(MessageState.trash_box + "/list");
    }

    @RequestMapping("clear/{state}")
    public String clear(@CurrentUser User user, @PathVariable("state") MessageState state, RedirectAttributes redirectAttributes) {
        messageService.clearBox(user.getId(), state);
        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(CLEAR_SUCCESS, MessageUtils.message(state.getInfo())));
        return redirect(MessageState.trash_box + "/list");
    }

    @RequestMapping("mark/read")
    public String markRead(@CurrentUser User user, @RequestParam(value = "ids", required = false) Long[] ids, @RequestParam("BackURL") String backURL, RedirectAttributes redirectAttributes) {
        messageService.markRead(user.getId(), ids);
        redirectAttributes.addFlashAttribute(Constants.MESSAGE, MessageUtils.message(MARK_AS_READ_SUCCESS));
        return redirect(backURL);
    }

    @RequestMapping(value = "/unreadCount")
    @ResponseBody
    public String unreadCount(@CurrentUser User user) {
        return String.valueOf(messageService.countUnread(user.getId()));
    }

    protected Message newModel() {
        try {
            return new Message();
        } catch (Exception e) {
            throw new IllegalStateException("can not instantiated model : " + Message.class, e);
        }
    }

    @RequestMapping(value = "/count")
    @ResponseBody
    public Object count() {
        return AjaxResponse.successWithData(messageService.countUnread(AppUtils.getCurrentUserId()));
    }
}
